package main

func main() {
	/*
		go.module
		1.主要解决两个问题：
			（1）准确的记录项目依赖
					指你的项目依赖哪些package、以及package的版本可以非常精准
			（2）可重复的构建
					指项目无论在谁的环境中（同平台）构建，其产物都是相同的

		2.module：一组 package 的集合，一起被标记版本，即是一个module

		3.仓库、module和package的关系：
			（1）一个仓库包含一个或多个 Go module（不推荐多个）
			（2）每个 Go module 包含一个或多个 Go package
			（3）每个 package 包含一个或多个 Go 源文件

		4.一个module的版本号必须遵循语义化规范
			`v(major).(minor).(patch)`       例如：`v0.1.0`
			major: 当发生不兼容的改动时才可以增加 major 版本
			minor: 当有新增特性时才可以增加该版本
			patch: 当有 bug 修复时才可以增加该版本

		5.一个项目若要使用go module，那么其本身需要先成为一个module
			（1）现在的版本已经不需要手动初始化了，在项目创建是，就自动生成 go.mod 了
				（手动生成的话，使用 go mod init moduleName）
			（2）go.mod 文件:
					模块名称、go的版本、该模块的依赖信息（依赖名称）。
					（前两个在创建项目生成go.mod文件时自动生成，最后一个在 go get 获取第三方包时自动生成）
				go.sum 文件:
					记录依赖包的 hash 值，用来确保依赖包没有被篡改
			（3）go get 外部依赖名:
					分析依赖情况，并自动下载依赖
						eg: go get github.com/codegangsta/inject

					go get 总是获取依赖的最新版本
					go get 会自动修改 go.mod 文件和 go.sum 文件（如果是第一次引用外部依赖，会自动生成）中的信息

		6.go.mod 文件中通过指令声明 module 信息，用于控制命令行工具进行版本选择
			（1）module: 声明 module 名称
					eg: module basic
					指定 module 的名字
			（2）require: 声明依赖以及其版本号
					eg: require github.com/google/uuid v1.1.1
					相当于告诉 go build 使用 require github.com/google/uuid 的 v1.1.1 版本进行编译
			（3）replace: 替换 require 中声明的依赖，使用另外的依赖及其版本号
					eg: replace github.com/google/uuid v1.1.1 => github.com/google/uuid v1.1.0
					正常情况下不需要使用 replace 来修改版本，直接修改 require 即可

					1.replace正常工作的条件：
						（1）仅在当前 module 为 main module（当前在编译的module） 时有效，
							如果其他项目引用该 module 则其他项目编译时，replace会被自动忽略
						（2）=> 前面的包及其版本号必须出现在 require 中才有效，否则也会被忽略
					2.replace 使用场景
						（1）替换无法下载的包：
								由于中国大陆网络问题，有些包无法顺利下载，比如goland.org组织下的包，但是这些包在GitHub有镜像，此时就可以使用github上的包来替换
								eg: replace golang.org/x/text v0.3.2 => github.com/golang/text v0.3.2
								注：import 后的包名不能替换，因为GitHub上的镜像包中的go.mod文件中定义的包名与原包名一样
						（2）调试依赖包
						（3）使用 fork 仓库
								有时在使用开源的依赖包时发现了bug，在开源版本还未修改或者没有新的版本发布时，
								你可以使用fork仓库，在fork仓库中进行bug fix。
								你可以在fork仓库上发布新的版本，并相应的修改go.mod来使用fork仓库。
						（4）禁止被依赖
								你的module不希望直接被引用
			（4）exclude: 禁用指定的依赖
					eg: exclude github.com/google/uuid v1.1.0
					表示不使用v1.1.0版本

					1.与replace类似，也仅在当前module为main module时有效，其他项目引用当前项目时，exclude指令会被忽略。

					2.exclude指令在实际的项目中很少被使用，因为很少会显式地排除某个包的某个版本，除非我们知道某个版本有严重bug。

					3.exclude 会排除指定版本
						虽然我们暂时没有使用uuid的v1.1.0版本，
						但如果将来引用了其他包，正好其他包引用了uuid的v1.1.0版本的话，此时添加的exclude指令就会跳过v1.1.0版本

		7. indirect: 间接依赖
			（1）间接依赖出现在 go.mod 文件的情况：
				1.直接依赖未启用 Go module
					Module A 依赖 B，但是 B 还未切换成 Module，也即没有go.mod文件，
					此时，当使用go mod tidy命令更新A的go.mod文件时，B的两个依赖B1和B2将会被添加到A的go.mod文件中
					（前提是A之前没有依赖B1和B2），并且B1 和B2还会被添加// indirect的注释。
				2.直接依赖 go.mod 文件中缺失部分依赖
					Module B虽然提供了go.mod文件中，但go.mod文件中只添加了依赖B1，
					那么此时A在引用B时，则会在A的go.mod文件中添加B2作为间接依赖，B1则不会出现在A的go.mod文件中

			（2）为什么要记录间接依赖
					Go module需要精确地记录软件的依赖情况，虽然此处记录了依赖B的版本号，但B的依赖情况没有记录下来，
					所以如果B的go.mod文件缺失了（或没有）这个信息，则需要在A的go.mod文件中记录下来。
					此时间接依赖的版本号将会根据Go module的版本选择机制确定一个最优版本

			（3）综上所述间接依赖出现在go.mod中，可以一定程度上说明依赖有瑕疵，要么是其不支持Go module，要么是其go.mod文件不完整。

			（4）如何查找间接依赖来源
				go mod why -m <pkg>
					eg: go mod why -m github.com/codegangsta/inject
				go mod why -m all
					可以分析所有依赖的依赖链

		8.版本选择机制：
			（1）依赖包版本约定
				go module 之前：
					Go 语言建议依赖包需要保持向后兼容，这包括可导出的函数、变量、类型、常量等不可以随便删除。
					以函数为例，如果需要修改函数的入参，可以增加新的函数而不是直接修改原有的函数。
				go module 之后：
					1.go module 开始关心依赖包版本管理系统中的版本号
					2.如果新 package 和旧 package 拥有相同的 import 路径，新 package 必须兼容旧的 package
					3.如果新 package 不能兼容旧的 package，那么新的 package 需要更换 import 路径
					4.go module 规定，如果 major 版本号大于 1，则major 版本号需要显式地标记在 module 名字中
						这样的好处是go module 会把它们视为不同的 module，甚至可以同时被引用
							比如Module github.com/RainbowMango/m，如果其版本号增长到v2.x.x时，
							其Module名字也需要相应的改变为： github.com/RainbowMango/m/v2

			（2）版本选择机制
				go的多个命令行工具都有自动选择依赖版本的能力，
					如 go build 和 go test，在源代码中增加了新的 import，这些命令将会自动选择一个最优的版本

				注：go.mod 如果已经标记了某个依赖的版本号，则这些命令不会主动更新 go.mod 中的版本号

				1.最新版本的选择
					当在源代码中新增加一个 import，如果go.mod中没有包含这个依赖，
					那么go build和go test这些命令就会去仓库中寻找最新的符合语义化版本规范的版本，并在go.mod中增加一条依赖
				2.最小版本选择
					有时在go.mod文件中的依赖包版本会随着引入其他依赖包而发生变化
					eg：Module A 依赖 Module M的v1.0.0版本，但之后 Module A 引入了 Module D，
						而Module D 依赖 Module M的v1.1.1版本，此时，由于依赖的传递，Module A也会选择v1.1.1版本。
					注：此时会自动选择最小可用的版本，而不是最新的tag版本

		9.incompatible：
		能否引用不兼容的包
				如果当前版本的 major 版本号大于 1，但是其 module 名字未改变（未遵循Goland所推荐的风格），我们称这个 module 为不规范的 module
				此时，这个不规范的 module 还是可以引用的，只是会在 go.mod 中增加 +incompatible（不兼容）标识
					eg：require github.com/RainbowMango/m v3.6.0+incompatible

		10.伪版本
			在实际项目中，有时不得不直接使用一个commit ID，
				比如某项目发布了v1.5.0版本，但随即又修复了一个bug（引入一个新的commit ID），
				而且没有发布新的版本。此时，如果我们希望使用最新的版本，就需要直接引用最新的commit ID，而不是之前的语义化版本v1.5.0。
				使用commit ID的版本在Go语言中称为pseudo-version，可译为"伪版本"。
				伪版本的版本号通常会使用vx.y.z-yyyymmddhhmmss-abcdefabcdef格式，
					其中vx.y.z看上去像是一个真实的语义化版本，但通常并不存在该版本，所以称为伪版本。
					另外abcdefabcdef表示某个commit ID的前12位，而yyyymmddhhmmss则表示该commit的提交时间，方便做版本比较。
						例如：require github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0

		11.依赖包存储
			（1）在GOPATH模式下，go get命令会将依赖包下载到$GOPATH/src/google目录中，
					该目录下只保存特定依赖包的一个版本
			（2）在GOMODULE模式下，go get命令会将依赖包下载到$GOPATH/pkg/mod目录下，并且按照依赖包的版本分别存放。
					该目录中可以存储特定依赖包的多个版本。
			（注：go get命令不指定特定版本时，默认会下载最新版本，即v1.1.1，如软件包有新版本发布，实验结果将有所不同。）

			（3）相较于GOPATH模式，GOMODULE有两处不同点：
				1.依赖包的目录中包含了版本号，每个版本占用一个目录；
				2.依赖包的特定版本目录中只包含依赖包文件，不包含.git目录

			（4）包名大小写敏感问题
				有时我们使用的包名中会包含大写字母，比如github.com/Azure/azure-sdk-for-go，
				GOMODULE模式下，在存储时会将包名做大小写编码处理，即每个大写字母将变与!+相应的小写字母，
					eg：github.com/Azure包在存储时将会被放置在$GOPATH/pkg/mod/github.com/!azure目录中。

				注：GOMODULE模式下，我们使用go get命令时，如果不小心将某个包名大小写搞错，
					eg：github.com/google/uuid写成github.com/google/UUID时，在存储依赖包时会严格按照go get命令指示的包名进行存储。

		12.go.sum
			（1）go.sum 文件记录：
				1.go.sum文件中每行记录由 module名、版本 和 哈希 组成，并由空格分开：
				<module> <version>[/go.mod] <hash>
					eg：github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
					    github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

				2.在Go module机制下，我们需要同时使用依赖包的名称和版本才可以准确的描述一个依赖，为了方便叙述，
					下面我们使用依赖包版本来指代依赖包名称和版本。
				正常情况下，每个依赖包版本会包含两条记录，第一条记录为该依赖包版本整体（所有文件）的哈希值，
				第二条记录仅表示该依赖包版本中go.mod文件的哈希值，如果该依赖包版本没有go.mod文件，则只有第一条记录。

				3.go.sum文件中记录的依赖包版本数量往往比go.mod文件中要多，这是因为二者记录的粒度不同导致的。
					go.mod只需要记录直接依赖的依赖包版本，只在依赖包版本不包含go.mod文件时候才会记录间接依赖包版本，
					而go.sum则是要记录构建用到的所有依赖包版本。

			（2）go.sum存在的意义在于，我们希望别人或者在别的环境中构建当前项目时所使用依赖包跟go.sum中记录的是完全一致的，从而达到一致构建的目的。
	*/
}
